<!DOCTYPE html>
<html>
<head>
	<title></title>
</head>
<body>
	<canvas id="cvs" width="600" height="600" style="background-color:#FAFAFA;border:1px solid red;"></canvas>
</body>
	<script type="text/javascript">
//ctx.globalCompositeOperation
//source-over
//destination-over
//source-atop
//destination-atop
//source-in
//destination-in
//source-out
//lighter
//copy
//xor
//context . globalAlpha [ = value ] //0-1.0之间的数据，设定图像的透明度
//gradient . addColorStop(offset, color) //在给定偏移的地方增加一个渐变颜色点，偏移量取值范围为0-1.0之间
//gradient = context . createLinearGradient(x0, y0,x1, y1) //建立一个线性渐变，
//gradient = context . createRadialGradient(x0, y0, r0, x1, y1, r1) //建立一个径向渐变
//pattern = context . createPattern(image, repetition)
//context . lineCap [ = value ] //返回或设置线段的箭头样式，仅有三个选项：butt(默认值),round,square;其他值忽略
//butt 每根线的头和尾都是长方形，也就是不做任何的处理，为默认值
//round 每根线的头和尾都增加一个半圆形的箭头
//square 每根线的头和尾都增加一个长方形，长度为线宽一半，高度为线宽
//context . lineJoin [ = value ] ///返回或设置线段的连接方式，仅有三个选项：miter(默认值),round，bevel；其他值忽略
//miter 线段在连接处外侧延伸直至交于一点，为默认值，外延效果受miterLimit的值影响，当外延交点距离大于限制值时，则表现为bevel风格
//round 连接处是一个圆角,圆的半径等于线宽
//bevel 连接处为斜角
//context . lineWidth [ = value ] //返回或设置线段的线宽，非大于0的值被忽略；默认值为1.0；
//context . miterLimit [ = value ] //返回或设置线段的连接处的斜率，非大于0的值被忽略；默认值为10.0。
//context . shadowBlur [ = value ] //返回或设置阴影模糊等级，非大于等于0的值被忽略；
//context . shadowColor [ = value ] //返回或设置阴影颜色
//context . shadowOffsetX [ = value ]
//context . shadowOffsetY [ = value ] //返回或设置阴影的偏移量
//context . clearRect(x, y, w, h) \\在给定的矩形内清除所有的像素为透明黑(transparent black)
//context . fillRect(x, y, w, h) //用当前的填充风格填充给定的区域
//context . strokeRect(x, y, w, h) //使用当前给定的线风格，绘制一个盒子区域，影响其绘制风格的有：strokeStyle，lineWidth，lineJoin，miterLimit（可能）。
//context . beginPath() //清空子路径
//context . closePath() //闭合路径
//stroke 方法使用lineWidth，lineCap，lineJoin，以及strokeStyle对所有的子路径进行填充。
//fill方  法使用fillStyle方式填充子路径，未闭合的子路径在填充式按照闭合方式填充，但并不影响实际的子路径集合。
//clip 方法使用计算所有的子路径而建立新的剪切区域，未闭合的子路径在填充式按照闭合方式填充，但并不影响实际的子路径集合，
//新的剪切区域将替换当前的剪切区域。裁切路径和普通的 canvas 图形差不多，不同的是它的作用是遮罩，用来隐藏没有遮罩的部分
//context . isPointInPath(x, y) 给定的坐标(x,y)是否在当前路径中，坐标（x，y）为绘图坐标系坐标，并不受转换的影响。
//context . rect(x, y, w, h)本方法建立二个新的子路径，第一个子路径包含四个点:（x，y），（x+w，y），（x+w，y+h），（x，y+h），四个点的连接方式为直线，该子路径被标示为闭合路径；最后再增加一个子路径，其仅有一个点(x，y)。
//context . arc(x, y, radius, startAngle, endAngle, anticlockwise)方法概述：本方法先增加一条直线到子路径列表，然后增加一个圆弧子路径到子路径列表。直线子路径是由上一个点到圆弧子路径的起始点，而圆弧则为按照给定的开始角度、结束角度和半径描述的按照给定的方向[布尔类型，anticlockwise-逆时针（true）]圆弧上；假如半径为负值，抛出INDEX_SIZE_ERR的异常；
//context . arcTo(x1, y1, x2, y2, radius)本方法绘制出子路径最后一个点(x0,y0)和（x1,y1）以及(x1,y1)和（x2,y2）构成的两条直线间半径为radius的最短弧线，并用直线连接(x0,y0)；假如半径为负值，
//bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
//quadraticCurveTo(cpx, cpy, x, y)
//上面分别是三次贝赛尔曲线和二次贝赛尔曲线的调用格式。其主要区别在于控制曲线的控制点式不一样的。其起始点均为子路径的最后一个点，结束点均为(x,y)；在最后均要把（x，y）点加入到子路径中。其绘制图形的例子如下，三次贝赛尔曲线有两个红点作为曲线平滑的控制点，而二次贝塞尔曲线仅有一个控制点。
//获得和设置文字设置： context.font[=value],
//获取或设置文字对齐方式：context.textAlign[=value]，取值如下：
// start 默认值，与canvas风格中的direction定义有关 end 与canvas风格中的direction定义有关 left 左 right 右 center 居中 
// 获得和设置文字对齐基线：context.textBaseline[=value],value的取值如下： alphabetic 默认值，alphabetic基线 top 文字矩形的顶部 bottom 文字矩形的底部 middle 文字矩形的中间 hanging Hanging基线 ideographic Ideographic基线
//绘制文字的方法：context.fillText(text,x,y[,maxWidth])
//context.strokeText(text,x,y[,maxWidth])
// 前一个方法为绘制填充的文字，后一个方法为对文字进行描边，不填充内部区域。
//按照当前字体对给定的文字进行测量：metrics = context.measureText(text),该方法返回一个TextMetrics对象，可以调用对象的width属性得到文字的宽度。
//要在绘图上下文中绘制图片，可以使用drawImage方法。该方法有三种不同的参数:
// drawImage(image,dx,dy)
// drawImage(image,dx,dy,dw,dh)
// drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh)
//imagedata = context . createImageData(sw, sh)
// imagedata = context . createImageData(imagedata)
// 方法概述：createImageData方法根据给定的CSS像素宽高或指定的imagedata具有的宽高建立一个ImageData对象，该对象为透明黑。该方法具体实例化一个新的空ImageData对象。
//imagedata = context . getImageData(sx,sy,sw, sh)
// 方法概述：getImageData方法根据给定的绘图画布矩形面积(sx,sy,sw,sh)，生成画布上该矩形面积的图形内容，并综合为ImageData对象返回。画布外的像素作为透明黑返回。
//imagedata = context . putImageData(imagedata, dx, dy [, dirtyX, dirtyY, dirtyWidth, dirtyHeight ])
// 方法概述：在绘图画布上绘制给定的ImageData对象。假如脏矩形被提供，则只有在脏矩形上面的像素被绘制。本方法对全局透明、阴影和全局组合属性均忽略。
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
	var cvs = document.getElementById('cvs');
	var ctx = cvs.getContext('2d');
	var CVS_WIDTH = 800;
	var CVS_HEIGHT = 800;
	var ROLL_DICE_TIME = 50;

	var BG_R = CVS_HEIGHT/2 - 10;
	var DICE_NUMBER = 16;
	var ROLL_NUMBER = 10;
	var SPEED = 50;
	var SPEED_MIN = 50;
	var G = 0.99;
	var dices;


	var DICE_R = 50;
	var DICE_R_2 = DICE_R * 2;
	var DICE_R_4 = DICE_R * 4;


	cvs.width = CVS_WIDTH;
	cvs.height = CVS_WIDTH;

	init(ctx);
	//drawBackground(ctx);
	loadDicesImage(function(imgs){
 		dice = new dice(imgs);

 		setInterval(function(){
			dice.rollUpdate();
			dice.rollDraw();
 		}, ROLL_DICE_TIME);
	});
	function init(ctx){
		//ctx.shadowBlur = 10;
		ctx.shadowColor = 'rgb(200,0,0)';
		//ctx.shadowOffsetX = 2;
		//ctx.shadowOffsetY = 2;
	}
	function drawBackground(ctx){
		ctx.save();
		ctx.beginPath();
		ctx.lineWidth = 1;
		ctx.strokeStyle = "red";
		drawArc(ctx, CVS_WIDTH/2, CVS_HEIGHT/2, BG_R, 0 , 360, true);
		ctx.stroke();
		ctx.restore();
	}
	function dice(imgsObj){
		var ds = [];
		var dataDS = [];
		var dsLength = 0;

		function create(num){
			num = num || 1;
			for(var i=0;i<num;i++){
				ds.push(getOne());
				dsLength++;
			}
			draw();
		}
		var MathPI_180 = Math.PI/180;

		//筛子随机生成
		function getOne(){
			var x = Math.random() * (CVS_WIDTH - DICE_R_4 - 1) + DICE_R_2;
			var y = Math.random() * (CVS_HEIGHT - DICE_R_4 - 1) + DICE_R_2;
			if(overlap({x:x, y:y})){
				return getOne();
			}
			var random = Math.random();
			var random2 = Math.random(2);
			var data = {
				//len: Math.random() * (BG_R - DICE_R*1.2) | 0,//半径
				//des: Math.random() * 360 | 0,//方向
				x: x,
				y: y,
				pot: (random * 6) | 0,//点数
				rollIndex: ((random * ROLL_NUMBER) + 1) | 0,//转动速度
				vx: (random*10%2 > 1?1:-1)*(random * SPEED + SPEED_MIN),//速度
				vy: (random2*10%2 > 1?1:-1)*(random2 * SPEED + SPEED_MIN)//速度
			}
			return data;
		}
		function overlap(data){
			for(var i=0;i<dsLength;i++){
				if(Math.abs(data.x - ds[i].x) < DICE_R_2 && Math.abs(data.y - ds[i].y) < DICE_R_2){
					return true;
				}
			}
			return false;
		}
		function draw(data){
			if(data){//单个
				ctx.drawImage(imgsObj['dice'][data['pot']], data.x - DICE_R, data.y - DICE_R, DICE_R*1.5, DICE_R*1.5);
			}else{
				for(var i=0;i<dsLength;i++){
					ctx.drawImage(imgsObj['dice'][ds[i]['pot']], ds[i].x - DICE_R, ds[i].y - DICE_R, DICE_R_2, DICE_R_2);
				}
			}
		}

		//筛子旋转
		var rollTimeHander;

		function rollStop(){
			clearInterval(rollTimeHander);
			draw();
		}

		function rollUpdate(){
			for(var i=0;i<dsLength;i++){
				var dsi = ds[i];
				if(dsi.vx === 0 && dsi.vy === 0){//停止

				}else{
					dsi['rollIndex'] = (dsi['rollIndex'] + 1)%ROLL_NUMBER;

					//碰撞检测
					/*for(var j=i+1;j<dsLength;j++){
						if(!ds[j]){
							break;
						}
						if(Math.abs(dsi.x - ds[j].x) < DICE_R_2 && Math.abs(dsi.y - ds[j].y) < DICE_R_2){
							if(ds[j].vx === 0 && ds[j].vy === 0){
								dsi.vx *= -G;
								dsi.vx *= -G;
							}else{
								var temp = dsi.vx;
								dsi.vx = ds[j].vx * G;
								ds[j].vx = temp * G;

								temp = dsi.vy;
								dsi.vy = ds[j].vy * G;
								ds[j].vy = temp * G;
							}
						}
					}*/
				}

				dsi.x += dsi.vx;
				dsi.y += dsi.vy;

				if(Math.abs(dsi.vx)<1.5){
					dsi.vx = 0;
				}else{
					dsi.vx *= G;
				}
				if(Math.abs(dsi.vx)<1.5){
					dsi.vy = 0;
				}else{
					dsi.vy *= G;
				}
				if(dsi.x<DICE_R){
					dsi.x = DICE_R;
					dsi.vx *= -G;
				}else if(dsi.x>CVS_WIDTH-DICE_R){
					dsi.x = CVS_WIDTH-DICE_R;
					dsi.vx *= -G;
				}
				if(dsi.y<DICE_R){
					dsi.y = DICE_R;
					dsi.vy *= -G;
				}else if(dsi.y>CVS_HEIGHT-DICE_R){
					dsi.y = CVS_HEIGHT-DICE_R;
					dsi.vy *= -G;
				}
			}
		}
		//重绘
		function rollDraw(){
			ctx.clearRect(0, 0, CVS_WIDTH, CVS_HEIGHT);
			for(var i=0;i<dsLength;i++){
				var dsi = ds[i];
				if(dsi.vx === 0 && dsi.vy === 0){
					draw(dsi);
				}else{
					ctx.drawImage(imgsObj['roll'][dsi['rollIndex']] , dsi.x - DICE_R, dsi.y - DICE_R, DICE_R_2, DICE_R_2);
				}
			}
		}

		create(DICE_NUMBER);
		return {data:ds, draw:draw, rollUpdate:rollUpdate, rollStop:rollStop, rollDraw:rollDraw};
	}
	function drawArc(ctx, width, height, r, begin, end, b){
		ctx.arc(width, height, r, Math.PI*(begin/180), Math.PI*(end/180), b);
	}
	//加载筛子资源
	function loadDicesImage(cb){
		var imgs = {dice:[], roll:[]};
		var img;
		var complete = 0;
		for(var i=0;i<ROLL_NUMBER;i++){
			img = new Image();
	        img.thisType = 'roll';
	        img.index = i + 1;
	        img.onreadystatechange = imgOnload;
	        img.onload = imgOnload;
	        img.src = 'dice/roll' + (i+1) +'.png';
		}
		for(var i=0;i<6;i++){
			img = new Image();
	        img.onreadystatechange = imgOnload;
	        img.thisType = 'dice';
	        img.index = i + 1;
	        img.onload = imgOnload;
	        img.src = 'dice/dice' + (i+1) +'.png';
		}
		function imgOnload(){
            if (this.complete || this.readyState == "complete" || this.readyState == "loaded") {
                complete++;
                imgs[this.thisType].push(this);
                if(complete === ROLL_NUMBER + 6){
                	cb(imgs);
                }
            }
		}
	}
</script></html>